﻿using System;
using System.Collections.Generic;
using System.Text;
using Vortex;
using Vortex.Drawing;
using Vortex.Input;

namespace Vortex.Tutorials.PhongBlinn {

	class LightSource {
		public float Intensity = 2.0f;
		public Vector3 Position;
		public Vector3 Color;
	}

	class Material {
		public float Exp = 10f;
		public float Diffuse = 0.7f;
		public float Specular = 0.7f;
	}

	/// <summary>
	/// Phong Blinn Lighting Demo is made by A.Shambalev
	/// </summary>
	class PhongBlinnDemo : Game {
		Texture _DiffuseTexture;
		Texture _NormalTexture;
		PixelShader _PhongBlinnShader;

		LightSource LightSource;
		Material Material;

		Vector4 AmbientColor;
		float AmbientPower;

		int WindowW = 800;
		int WindowH = 600;
		float WindowHalfW;
		float WindowHalfH;

		public PhongBlinnDemo() {
			WindowHalfW = WindowW / 2.0f;
			WindowHalfH = WindowH / 2.0f;

			Window.Size = new System.Drawing.Size(WindowW, WindowH);
			Window.VerticalSync = true;
			ChangeDisplayModeOnAltEnter = false;

			LightSource = new LightSource();
			LightSource.Position = new Vector3(WindowHalfW, WindowHalfH, 60);
			LightSource.Color = new Vector3(1, 1, 1);

			Material = new Material();

			AmbientColor = new Vector4(0.3f, 0.5f, 1.0f, 0.6f);
			AmbientPower = 0.5f;

			Keyboard.KeyUp += delegate(VirtualKey key) {
				if (key == VirtualKey.Escape) {
					Terminate();
				}
			};

			Keyboard.KeyDown += delegate(VirtualKey key) {
				switch (key) {
					case VirtualKey.Q:
						Material.Exp = Math.Min(Material.Exp + 0.5f, 60f);
						break;
					case VirtualKey.A:
						Material.Exp = Math.Max(Material.Exp - 0.5f, 0f);
						break;
					case VirtualKey.W:
						Material.Diffuse = Math.Min(Material.Diffuse + 0.1f, 1f);
						break;
					case VirtualKey.S:
						Material.Diffuse = Math.Max(Material.Diffuse - 0.1f, 0f);
						break;
					case VirtualKey.E:
						Material.Specular = Math.Min(Material.Specular + 0.1f, 1f);
						break;
					case VirtualKey.D:
						Material.Specular = Math.Max(Material.Specular - 0.1f, 0f);
						break;
					case VirtualKey.R:
						AmbientPower = Math.Min(AmbientPower + 0.05f, 1f);
						break;
					case VirtualKey.F:
						AmbientPower = Math.Max(AmbientPower - 0.05f, 0f);
						break;
					case VirtualKey.T:
						LightSource.Position.Z = Math.Min(LightSource.Position.Z + 1f, 200f);
						break;
					case VirtualKey.G:
						LightSource.Position.Z = Math.Max(LightSource.Position.Z - 1f, 0f);
						break;
					case VirtualKey.Y:
						LightSource.Intensity = Math.Min(LightSource.Intensity + 0.1f, 10f);
						break;
					case VirtualKey.H:
						LightSource.Intensity = Math.Max(LightSource.Intensity - 0.1f, 0f);
						break;
				}
			};

			Mouse.Move += delegate(MouseMoveActionInfo info) {
				LightSource.Position = new Vector3(info.Location.X, info.Location.Y, LightSource.Position.Z);
			};
		}

		protected override void Load() {
			//set path alias
			Vortex.IO.FileSystem.SetPathAlias("demodata", "./data/PhongBlinnDemo");
			//load resources using path alias
			_DiffuseTexture = new Texture("{demodata}/diffuse.jpg");
			_NormalTexture = new Texture("{demodata}/normal.jpg");
			_PhongBlinnShader = new PixelShader("{demodata}/phongblinn.psh");
		}

		protected override void Unload() {
			_DiffuseTexture.Dispose();
			_NormalTexture.Dispose();
			_PhongBlinnShader.Dispose();
		}

		protected override void Update(GameTime time) {
		}

		protected override void Render(Canvas2D canvas) {
			canvas.Clear(ColorU.Black);

			// Passing light parameters
			_PhongBlinnShader.Params.Set("LightColor", LightSource.Color);
			_PhongBlinnShader.Params.Set("LightPosition", LightSource.Position);
			_PhongBlinnShader.Params.Set("Intensity", LightSource.Intensity);

			// Passing material parameters
			_PhongBlinnShader.Params.Set("Exp", Material.Exp);
			_PhongBlinnShader.Params.Set("DiffusePower", Material.Diffuse);
			_PhongBlinnShader.Params.Set("SpecularPower", Material.Specular);

			// Passing object parameters
			_PhongBlinnShader.Params.Set("ObjectSize", new Vector2(_DiffuseTexture.Size.Width, _DiffuseTexture.Size.Height));
			_PhongBlinnShader.Params.Set("ObjPosition", new Vector2(WindowHalfW - _DiffuseTexture.Size.Width / 2, WindowHalfH - _DiffuseTexture.Size.Height / 2));

			// Passing ambient light parameters
			_PhongBlinnShader.Params.Set("AmbientColor", AmbientColor);
			_PhongBlinnShader.Params.Set("AmbientPower", AmbientPower);

			using (canvas <= _PhongBlinnShader <= _NormalTexture * 2) {
				canvas.DrawSprite(new Vector2(WindowHalfW, WindowHalfH), _DiffuseTexture.ToSprite().Resize(384, 384), ColorU.White);
			}

			canvas.DrawTextLayout(
				SpriteFont.SmallConsole,
				canvas.Region.Deflate(new Vector2(4, 4)),
				string.Format(
				"Q, A - Material specular exponent (Value = {0:F2})\n" +
				"W, S - Material diffuse power (Value = {1:F2})\n" +
				"E, D - Material specular power (Value = {2:F2})\n" +
				"R, F - Ambient power (Value = {3:F2})\n" +
				"T, G - Light source Z position (Value = {4:F2})\n" +
				"Y, H - Light intensity (Value = {5:F2})\n\n" +
				"Esc - Exit"
				,
				Material.Exp, Material.Diffuse, Material.Specular, AmbientPower, LightSource.Position.Z, LightSource.Intensity),
				TextLayout.Left | TextLayout.Bottom,
				ColorU.White
			);

		}
	}

}
